package com.jacobson.huobi.websocket

import com.jacobson.core.widget.logger.LoggerFactory
import com.jacobson.core.widget.storage.GlobalStorage
import com.jacobson.huobi.Constants
import com.jacobson.huobi.HuobiApplication
import com.jacobson.huobi.common.constants.AlarmTypeConstant
import com.jacobson.huobi.common.constants.CommonConstant
import com.jacobson.huobi.common.constants.LinkStatusConstant
import com.jacobson.huobi.common.expansion.currentTimeSecond
import com.jacobson.huobi.common.expansion.isAlarm
import com.jacobson.huobi.common.expansion.isBuyAlarm
import com.jacobson.huobi.common.expansion.isSellAlarm
import com.jacobson.huobi.entities.ActionEnum
import com.jacobson.huobi.entities.AlarmInfo
import com.jacobson.huobi.entities.SymbolInfo
import com.jacobson.huobi.entities.events.UpdateEvent
import com.jacobson.huobi.entities.events.WebsocketEvent
import com.jacobson.huobi.entities.websocket.HeartbeatAction
import com.jacobson.huobi.entities.websocket.Protocol
import com.jacobson.huobi.entities.websocket.RefreshSymbolResp
import com.jacobson.huobi.utils.*
import org.simple.eventbus.EventBus
import org.simple.eventbus.Subscriber
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.LinkedBlockingDeque
import kotlin.collections.ArrayList
import kotlin.math.max
import kotlin.math.min

object WebsocketManager {
    private val logger = LoggerFactory.getLogger()
    private const val CONNECT_HEARTBEAT_UNCHECK = -4
    private const val CONNECT_DISCONNECT = -3  //连接断开,请检查网络
    private const val CONNECT_ERROR = -2 //连接失败,请检查网络
    private const val CONNECT_TIMEOUT = -1 //连接超时
    private const val CONNECTING = 1 //倒计时 连接中
    private const val CONNECTED = 2 //连接完成
    private const val DEFAULT_CUT_DOWN_TIMES = 40
    private const val HEARTBEAT_TIMEOUT = 13

    private var websocket: WebsocketClient?? = null
    @field:Transient
    private var isWebsocketThreadStarted = false
    @field:Transient
    private var needStopWebsocketThread = false
    private var lastHeartbeatResponseTimestamp = 0L
    private val waitToSendMessageQueue = LinkedBlockingDeque<Protocol>()

    init {
        EventBus.getDefault().register(this)
    }

    /** 发送消息 */
    fun sendMessage(request: Protocol) {
        waitToSendMessageQueue.offer(request)
    }

    @Synchronized
    fun startWebsocketConnectThread() {
        if (isWebsocketThreadStarted) {
            return
        }
        isWebsocketThreadStarted = true
        Thread {
            needStopWebsocketThread = false
            var retryTimes = 0
            while (true) {
                try {
                    websocket = WebsocketClient()
                    websocket!!.connect()
                    syncWaitConnected(
                            websocket!!,
                            retryTimes
                    ).takeIf { it }?.let {
                        retryTimes = 0
                        syncKeepHeartbeatAndSendMessage(websocket!!)
                    }
                    websocket.takeIf { it!!.isOpen }?.closeBlocking()
                    logger.w("第${retryTimes}次等待重连： ${min(30000L, retryTimes * retryTimes * 1000L)}")
                    retryTimes++
                    Thread.sleep(min(30000L, retryTimes * retryTimes * 1000L))
                } catch (ex: Exception) {
                    ex.printStackTrace()
                }
            }
        }.start()


    }

    @Subscriber
    private fun onHeartbeatResponse(heartbeatResponse: HeartbeatAction) {
        lastHeartbeatResponseTimestamp = max(
                heartbeatResponse.ts,
                lastHeartbeatResponseTimestamp
        )
    }

    @Subscriber
    @Suppress("UNCHECKED_CAST")
    private fun onRefreshSymbolResp(refreshSymbolResp: RefreshSymbolResp) {
        if (refreshSymbolResp.code==-10312){
            TokenStorageUtil.save("")
            EventBus.getDefault().post(UpdateEvent("", false, 1))
            return
        }
        var localList = GlobalStorage.get(Constants.KEY_SYMBOL_LIST) as MutableList<SymbolInfo>
        var list: MutableList<SymbolInfo> = arrayListOf()
        localList.addAll(refreshSymbolResp.data)
        for (i in localList.indices) {

            var temp = localList[i]
            for (j in localList.indices) {
                if (i < j && localList[i].currency == localList[j].currency) {
                    temp = localList[j]
                }
            }
            var isExists = false
            for (j in list.indices) {
                if (list[j].currency == temp.currency) {
                    isExists = true
                }
            }
            if (!isExists) {
                list.add(temp)
            }
        }
        //测试语音功能
//        if (refreshSymbolResp.code == ActionEnum.SUBSCRIBE_REFRESH_SYMBOL.value) {
//            playBuyAudio(localList[0])
//        }
        if (refreshSymbolResp.code == ActionEnum.PUSH.value) {
            // 报警和发通知

            startAlarmAndNotification(refreshSymbolResp.data)
            GlobalStorage.put(CommonConstant.KEY_ALARM_SYMBOL_LIST, refreshSymbolResp.data)
            EventBus.getDefault().post(UpdateEvent("", false, 2))
        } else {
            EventBus.getDefault().post(UpdateEvent("", false, 1))
        }

        GlobalStorage.put(Constants.KEY_SYMBOL_LIST, list)

    }

    /** 检测心跳 */
    private fun syncKeepHeartbeatAndSendMessage(websocket: WebsocketClient) {
        lastHeartbeatResponseTimestamp = currentTimeSecond()
        var counter = 0
        while (!needStopWebsocketThread && websocket.isOpen
                && currentTimeSecond() - lastHeartbeatResponseTimestamp <= HEARTBEAT_TIMEOUT
        ) {
            if ((counter % 20) == 0) {
                // 每5秒发送一次心跳
                websocket.sendProtocol(ProtocolFactory.ofHeartbeat())
            }
            if ((counter % 240) == 0 && GlobalStorage.get(Constants.KEY_IS_MAIN_FRONT) == true) {
                // 每60秒发送一次订阅
                websocket.sendProtocol(ProtocolFactory.ofSubscribe())
            }

            if (waitToSendMessageQueue.isNotEmpty()) {
                val message = waitToSendMessageQueue.poll()
                websocket.sendProtocol(message)
            }
            counter++
            Thread.sleep(250)
        }
        if (websocket.isOpen && currentTimeSecond() - lastHeartbeatResponseTimestamp > HEARTBEAT_TIMEOUT) {
            EventBus.getDefault().post(WebsocketEvent(CONNECT_HEARTBEAT_UNCHECK, "连接心跳检测失败"))
            return
        }
        EventBus.getDefault().post(WebsocketEvent(CONNECT_DISCONNECT, "连接断开,请检查网络"))
    }

    /**
     * 同步等待连接完成
     */
    private fun syncWaitConnected(
            websocket: WebsocketClient,
            retryTimes: Int
    ): Boolean {
        var cutDownTimes = DEFAULT_CUT_DOWN_TIMES
        while (!needStopWebsocketThread && !websocket.isOpen && cutDownTimes > 0) {
            if ((cutDownTimes % 4) == 0) {
                EventBus.getDefault().post(
                        WebsocketEvent(
                                CONNECTING,
                                "倒计时",
                                intArrayOf(cutDownTimes / 4, retryTimes)
                        )
                )
            }
            cutDownTimes--
            Thread.sleep(250)
        }
        if (needStopWebsocketThread) {
            EventBus.getDefault().post(WebsocketEvent(CONNECT_ERROR, "连接失败,请检查网络"))
            return false
        }
        if (cutDownTimes == 0 && !websocket.isOpen) {
            EventBus.getDefault().post(WebsocketEvent(CONNECT_TIMEOUT, "连接超时"))
            return false
        }
        EventBus.getDefault().post(WebsocketEvent(CONNECTED, "连接完成"))
        return true
    }

    /** 启动通知和报警 */
    @Synchronized
    private fun startAlarmAndNotification(symbolInfoList: List<SymbolInfo>) {
        val data = ArrayList<SymbolInfo>()
        symbolInfoList.forEach { symbolInfo ->
            if (symbolInfo.isAlarm() && symbolInfo.realtimePrimary) {
                val key = symbolInfo.market + symbolInfo.currency + symbolInfo.transCurrency
                val lastTs = lastAlarmTs[key]
                if (System.currentTimeMillis() - (lastTs ?: 0) <= 10000) {
                    // 过滤重复报警 ==> 间隔小于等于3秒的同一个市场同一个交易对报警，不连续累计
                    return@forEach
                }
                lastAlarmTs[key] = System.currentTimeMillis()
                data.add(symbolInfo)

            }
        }
        if (data.isNullOrEmpty()) {
            // 报警数据为空，返回
            return
        }
        playAudio(data)
        if (GlobalStorage.get(Constants.KEY_IS_MAIN_FRONT) == true) {
            // 应用在前台,不发送通知
            return
        }
        // 发送通知
        NotificationHelper.sendAlarmNotification(HuobiApplication.getInstance(), data)
    }

    private fun playAudio(data: ArrayList<SymbolInfo>) {
        if (SettingProperties.isMute()) {
            // 静音，不播放
            return
        }
        data.forEach { symbolInfo ->
            when {
                symbolInfo.isBuyAlarm() -> {
                    // 买入报警
                    playBuyAudio(symbolInfo)
                }
                symbolInfo.isSellAlarm() -> {
                    // 卖出报警
                    playSellAudio(symbolInfo)
                }
            }
        }

    }

    private fun playBuyAudio(symbolInfo: SymbolInfo) {
        playCommonAudio(
                SettingProperties.isUseTtsBuy(),
                SettingProperties.getRingtoneBuy(),
                symbolInfo
        )
    }

    private fun playSellAudio(symbolInfo: SymbolInfo) {
        playCommonAudio(
                SettingProperties.isUseTtsSell(),
                SettingProperties.getRingtoneSell(),
                symbolInfo
        )
    }

    private fun playCommonAudio(useTts: Boolean, ringtone: String?, symbolInfo: SymbolInfo) {
//        var alarmType = AlarmTypeConstant.getDescByValue(symbolInfo.alarmH1.type)
//        var ttsItems = SettingProperties.getRingtoneTTSItems()

        var alarmSetting = SettingProperties.getAlramSetting()
        val data = java.util.ArrayList<AlarmInfo>()
        if(alarmSetting.equals("1H报警")){
            if(symbolInfo.alarmH1.status==2 && LinkStatusConstant.getAlarmH1Code(symbolInfo.alarmH1.extraSign)) {
                data.add(symbolInfo.alarmH1)
            }
        }else if(alarmSetting.equals("4H报警")){
            if(symbolInfo.alarmH1.status==2 && LinkStatusConstant.getAlarmH4Code(symbolInfo.alarmH1.extraSign)){
                data.add(symbolInfo.alarmH1)
            }
            if(symbolInfo.alarmH4.status==2 && LinkStatusConstant.getAlarmH4Code(symbolInfo.alarmH4.extraSign)){
                data.add(symbolInfo.alarmH4)
            }
        }else if(alarmSetting.equals("12H报警")){
            if(symbolInfo.alarmH1.status==2 && LinkStatusConstant.getAlarmH12Code(symbolInfo.alarmH1.extraSign)){
                data.add(symbolInfo.alarmH1)
            }
            if(symbolInfo.alarmH4.status==2 && LinkStatusConstant.getAlarmH12Code(symbolInfo.alarmH4.extraSign)){
                data.add(symbolInfo.alarmH4)
            }
            if(symbolInfo.alarmH12.status==2 && LinkStatusConstant.getAlarmH12Code(symbolInfo.alarmH12.extraSign)){
                data.add(symbolInfo.alarmH12)
            }
        }else if(alarmSetting.equals("一天报警")){
            if(symbolInfo.alarmH1.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH1.extraSign)){
                data.add(symbolInfo.alarmH1)
            }
            if(symbolInfo.alarmH4.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH4.extraSign)){
                data.add(symbolInfo.alarmH4)
            }
            if(symbolInfo.alarmH12.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH12.extraSign)){
                data.add(symbolInfo.alarmH12)
            }
            if(symbolInfo.alarmDay.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmDay.extraSign)){
                data.add(symbolInfo.alarmDay)
            }
        }else if(alarmSetting.equals("全部")){
            if(symbolInfo.alarmH1.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH1.extraSign)){
                data.add(symbolInfo.alarmH1)
            }
            if(symbolInfo.alarmH4.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH4.extraSign)){
                data.add(symbolInfo.alarmH4)
            }
            if(symbolInfo.alarmH12.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmH12.extraSign)){
                data.add(symbolInfo.alarmH12)
            }
            if(symbolInfo.alarmDay.status==2 && LinkStatusConstant.getAlarmDayCode(symbolInfo.alarmDay.extraSign)){
                data.add(symbolInfo.alarmDay)
            }
        }
        data?.forEachIndexed {
                index, alarmdata ->

            var isExistsTTSItems: Boolean = SettingProperties.getRingtoneTTSItems().indexOf(
                AlarmTypeConstant.getDescByValue(alarmdata.type)
            ) >= 0
            if (useTts && isExistsTTSItems) {
//        if (useTts && SettingProperties.getRingtoneTTSItems().indexOf(AlarmTypeConstant.getDescByValue(symbolInfo.alarmH1.type)) > 0) {
                // 使用语音播报的方式
                val builder = StringBuilder()
                String.format(
                    "%1\$s%2\$s",
                    symbolInfo.currency,
                    symbolInfo.transCurrency
                ).toUpperCase(Locale.getDefault()).toCharArray().forEach {
                    builder.append(it).append(",")
                }
                builder.append(AlarmTypeConstant.getDescByValue(alarmdata.type))
                TTSHelper.startTTS(builder.toString())
                return
            }
            // 使用通知铃声来报警
            SoundUtil.getInstance().play(ringtone)
        }

    }


    private val lastAlarmTs = ConcurrentHashMap<String, Long>()
    fun closeConnect() {
        websocket!!.close()
    }
}
